/*
 * CartNetwork.java
 * 
 * Created by demory on Mar 28, 2009, 1:33:15 PM
 * 
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.fpdev.apps.cart.network;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.fpdev.apps.cart.CartDocument;
import org.fpdev.util.FPUtil;
import org.jgrapht.graph.Pseudograph;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 *
 * @author demory
 */
public class CartNetwork {

  private CartDocument doc_;

  private Pseudograph<AnchorPoint, Corridor> graph_;

  private Map<Integer, AnchorPoint> points_;
  private Map<Integer, Corridor> corridors_;
  private Map<Integer, Line> lines_;

  private int maxPointID_, maxCorridorID_, maxLineID_;

  public CartNetwork(CartDocument doc) {
    doc_ = doc;
    clear();
  }

  public void clear() {
    graph_= new Pseudograph<AnchorPoint, Corridor>(Corridor.class);
    points_ = new HashMap<Integer, AnchorPoint>();
    corridors_ = new HashMap<Integer, Corridor>();
    lines_ = new HashMap<Integer, Line>();
    maxPointID_ = maxCorridorID_ = maxLineID_= 0;
  }

  public Rectangle2D.Double getBoundingBox() {
    double minx = Double.MAX_VALUE, miny = Double.MAX_VALUE;
    double maxx = Double.MIN_VALUE, maxy = Double.MIN_VALUE;
    for(AnchorPoint pt : points_.values()) {
      minx = Math.min(minx, pt.getX());
      miny = Math.min(miny, pt.getY());
      maxx = Math.max(maxx, pt.getX());
      maxy = Math.max(maxy, pt.getY());
    }
    return new Rectangle2D.Double(minx, miny, maxx-minx, maxy-miny);
  }
  
  // ANCHOR POINT METHODS

  public Collection<AnchorPoint> getAnchorPoints() {
    return points_.values();
  }

  public void addAnchorPoint(AnchorPoint point) {
    graph_.addVertex(point);
    points_.put(point.getID(), point);
  }

  public int newAnchorPointID() {
    return ++maxPointID_;
  }

  public void deleteAnchorPoint(AnchorPoint point) {
    for(Corridor c : new HashSet<Corridor>(point.getCorridors())) {
      deleteCorridor(c);
    }
    graph_.removeVertex(point);
    points_.remove(point.getID());
  }

  public AnchorPoint getPointAtXY(double x, double y, double tol) {
    AnchorPoint best = null;

    double minDist = tol;
    for(AnchorPoint pt : getAnchorPoints()) {
      double dist = FPUtil.magnitude(x, y, pt.getX(), pt.getY());
      if(dist < minDist) {
        best = pt;
        minDist = dist;
      }
    }

    return best;
  }

  // CORRIDOR METHODS

  public Collection<Corridor> getCorridors() {
    return corridors_.values();
  }

  public Collection<Corridor> incidentCorridors(AnchorPoint point) {
    return graph_.edgesOf(point);
  }

  public int newCorridorID() {
    return ++maxCorridorID_;
  }

  public void addCorridor(Corridor corr) {
    graph_.addEdge(corr.fPoint(), corr.tPoint(), corr);
    corridors_.put(corr.getID(), corr);
  }

  public void deleteCorridor(Corridor corr) {
    corr.unregisterFromEndpoints();
    graph_.removeEdge(corr);
    corridors_.remove(corr.getID());
  }
  
  public Corridor getCorridorAtXY(double x, double y, double tol) {
    Corridor best = null;
    double minDist = tol;
    for(Corridor c : getCorridors()) {
      if(c.isStraight()) {
        double dist = FPUtil.distToSegment(x, y, c.x1(), c.y1(), c.x2(), c.y2());
        if(dist < minDist) {
          minDist = dist;
          best = c;
        }
      }
      else { // "bent" corridor
        Point2D.Double e = c.getElbow();
        double d1 = FPUtil.distToSegment(x, y, c.x1(), c.y1(), e.x, e.y);
        double d2 = FPUtil.distToSegment(x, y, e.x, e.y, c.x2(), c.y2());
        double dist = Math.min(d1, d2);
        if(dist < minDist) {
          minDist = dist;
          best = c;
        }
      }
    }
    return best;
  }

  // LINE METHODS

  public Collection<Line> getLines() {
    return lines_.values();
  }

  public int newLineID() {
    return ++maxLineID_;
  }

  public void addLine(Line line) {
    lines_.put(line.getID(), line);
  }

  public void deleteLine(Line line_) {
    lines_.remove(line_.getID());
  }

  public void rebundle() {
    new Bundler(this);
  }


  // FILE I/O METHODS

  public void readFromXML(Node cartNode) {

    //try {
    clear();

    NodeList docNodes = cartNode.getChildNodes();
    for (int i = 0; i < docNodes.getLength(); i++) {
      if(docNodes.item(i).getNodeName().equals("points")) {
        NodeList pointNodes = docNodes.item(i).getChildNodes();
        int maxID = 0;
        for (int ip = 0; ip < pointNodes.getLength(); ip++) {
          if(pointNodes.item(ip).getNodeName().equals("point")) {
            Node pointNode = pointNodes.item(ip);
            NamedNodeMap attributes = pointNode.getAttributes();
            int id = new Integer(attributes.getNamedItem("id").getNodeValue()).intValue();
            if(id > maxID) maxID = id;
            double x = new Double(attributes.getNamedItem("x").getNodeValue()).doubleValue();
            double y = new Double(attributes.getNamedItem("y").getNodeValue()).doubleValue();
            addAnchorPoint(new AnchorPoint(id, x, y));
          }
        }
        maxPointID_ = maxID;
      }
      if(docNodes.item(i).getNodeName().equals("corridors")) {
        NodeList corrNodes = docNodes.item(i).getChildNodes();
        int maxID = 0;
        for (int ic = 0; ic < corrNodes.getLength(); ic++) {
          if(corrNodes.item(ic).getNodeName().equals("corridor")) {
            Node corrNode = corrNodes.item(ic);
            NamedNodeMap attributes = corrNode.getAttributes();
            int id = new Integer(attributes.getNamedItem("id").getNodeValue()).intValue();
            if(id > maxID) maxID = id;
            int fpoint = new Integer(attributes.getNamedItem("fpoint").getNodeValue()).intValue();
            int tpoint = new Integer(attributes.getNamedItem("tpoint").getNodeValue()).intValue();
            addCorridor(new Corridor(id, points_.get(fpoint), points_.get(tpoint), false));
          }
        }
        maxCorridorID_ = maxID;
      }
      if(docNodes.item(i).getNodeName().equals("lines")) {
        NodeList lineNodes = docNodes.item(i).getChildNodes();
        int maxID = 0;
        for (int il = 0; il < lineNodes.getLength(); il++) {
          if(lineNodes.item(il).getNodeName().equals("line")) {
            Node lineNode = lineNodes.item(il);
            NamedNodeMap attributes = lineNode.getAttributes();
            int id = new Integer(attributes.getNamedItem("id").getNodeValue()).intValue();
            System.out.println("loading line "+id);
            if(id > maxID) maxID = id;
            String name = attributes.getNamedItem("name").getNodeValue();

            Line line = new Line(id, name);

            if(attributes.getNamedItem("style") != null) {
              int styleID = new Integer(attributes.getNamedItem("style").getNodeValue()).intValue();
              if(styleID > 0) line.setStyle(doc_.getStyles().getStyle(styleID));
            }

            int openMonth = 0, openYear = 0;
            if(attributes.getNamedItem("opendate") != null) {
              String opendate = attributes.getNamedItem("opendate").getNodeValue();
              if(opendate.length() == 6) {
                openMonth = Integer.parseInt(opendate.substring(0, 2));
                openYear = Integer.parseInt(opendate.substring(2, 6));
              }
            }

            String[] corrIDs = lineNode.getFirstChild().getNodeValue().split(",");
            for(String corrID : corrIDs) {
              Corridor corr = corridors_.get(new Integer(corrID));
              line.initCorridor(corr);//, offset, offset, openMonth, openYear);
              System.out.println(" added corr "+corrID);
            }

            //line.initBaseOffset(offset);

            addLine(line);
          }
        }
        maxLineID_ = maxID;
      }
    }
    /*} catch(Exception e) {
      e.printStackTrace();
    }*/
  }
  
  public String getXML(String indent) {
    String xml = "";

    xml += indent+"<points>\n";
    for(AnchorPoint ap : getAnchorPoints()) {
      xml += indent+"  "+ap.getXML();
    }
    xml += indent+"</points>\n";
    xml += indent+"<corridors>\n";
    for(Corridor corr : getCorridors()) {
      xml += indent+"  "+corr.getXML();
    }
    xml += indent+"</corridors>\n";

    xml += indent+"<lines>\n";
    for(Line line : getLines()) {
      xml += line.getXML(indent+"  ");
    }
    xml += indent+"</lines>\n";

    return xml;
  }


}
